1   /*
2    * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  import sun.management.jmxremote.ConnectorBootstrap;
24  
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.InputStream;
28  import java.io.FilenameFilter;
29  import java.io.IOException;
30  
31  import java.util.Properties;
32  import java.util.Iterator;
33  import java.util.Set;
34  import java.util.Arrays;
35  import java.util.ArrayList;
36  import java.util.HashMap;
37  import java.util.Map;
38  import java.util.Enumeration;
39  
40  import javax.management.remote.*;
41  import javax.management.*;
42  
43  import sun.management.AgentConfigurationError;
44  
45  import util.TestLogger;
46  
47  /**
48   * <p>This class implements unit test for RMI Bootstrap.
49   * When called with no arguments main() looks in the directory indicated
50   * by the "test.src" system property for files called management*ok.properties
51   * or management*ko.properties. The *ok.properties files are assumed to be
52   * valid Java M&M config files for which the bootstrap should succeed.
53   * The *ko.properties files are assumed to be configurations for which the
54   * bootstrap & connection test will fail.</p>
55   *
56   * <p>The rmi port number can be specified with the "rmi.port" system property.
57   * If not, this test will use 12424</p>
58   *
59   * <p>When called with some argument, the main() will interprete its args to
60   * be Java M&M configuration file names. The filenames are expected to end
61   * with ok.properties or ko.properties - and are interpreted as above.</p>
62   *
63   * <p>Note that a limitation of the RMI registry (bug 4267864) prevent
64   * this test from succeeding if more than 1 configuration is used.
65   * As long as 4267864 isn't fix, this test must be called as many times
66   * as needed but with a single argument (no arguments, or several arguments
67   * will fail).</p>
68   *
69   * <p>Debug traces are logged in "sun.management.test"</p>
70   **/
71  public class RmiBootstrapTest {
72  
73      static TestLogger log =
74          new TestLogger("RmiBootstrapTest");
75  
76      /**
77       * When launching several registries, we increment the port number
78       * to avoid falling into "port number already in use" problems.
79       **/
80      static int testPort = 0;
81  
82      /**
83       * Default values for RMI configuration properties.
84       **/
85      public static interface DefaultValues {
86          public static final String PORT="0";
87          public static final String CONFIG_FILE_NAME="management.properties";
88          public static final String USE_SSL="true";
89          public static final String USE_AUTHENTICATION="true";
90          public static final String PASSWORD_FILE_NAME="jmxremote.password";
91          public static final String ACCESS_FILE_NAME="jmxremote.access";
92          public static final String KEYSTORE="keystore";
93          public static final String KEYSTORE_PASSWD="password";
94          public static final String TRUSTSTORE="truststore";
95          public static final String TRUSTSTORE_PASSWD="trustword";
96          public static final String SSL_NEED_CLIENT_AUTH="false";
97      }
98  
99      /**
100      * Names of RMI configuration properties.
101      **/
102     public static interface PropertyNames {
103         public static final String PORT=
104             "com.sun.management.jmxremote.port";
105         public static final String CONFIG_FILE_NAME=
106             "com.sun.management.config.file";
107         public static final String USE_SSL=
108             "com.sun.management.jmxremote.ssl";
109         public static final String USE_AUTHENTICATION=
110             "com.sun.management.jmxremote.authenticate";
111         public static final String PASSWORD_FILE_NAME=
112             "com.sun.management.jmxremote.password.file";
113         public static final String ACCESS_FILE_NAME=
114             "com.sun.management.jmxremote.access.file";
115         public static final String INSTRUMENT_ALL=
116             "com.sun.management.instrumentall";
117         public static final String CREDENTIALS =
118             "jmx.remote.credentials";
119         public static final String KEYSTORE=
120             "javax.net.ssl.keyStore";
121         public static final String KEYSTORE_PASSWD=
122             "javax.net.ssl.keyStorePassword";
123         public static final String TRUSTSTORE=
124             "javax.net.ssl.trustStore";
125         public static final String TRUSTSTORE_PASSWD=
126             "javax.net.ssl.trustStorePassword";
127         public static final String SSL_ENABLED_CIPHER_SUITES =
128             "com.sun.management.jmxremote.ssl.enabled.cipher.suites";
129         public static final String SSL_ENABLED_PROTOCOLS =
130             "com.sun.management.jmxremote.ssl.enabled.protocols";
131         public static final String SSL_NEED_CLIENT_AUTH =
132             "com.sun.management.jmxremote.ssl.need.client.auth";
133     }
134 
135     /**
136      * A filter to find all filenames who match <prefix>*<suffix>.
137      * Note that <prefix> and <suffix> can overlap.
138      **/
139     private static class ConfigFilenameFilter implements FilenameFilter {
140         final String suffix;
141         final String prefix;
142         ConfigFilenameFilter(String prefix, String suffix) {
143             this.suffix=suffix;
144             this.prefix=prefix;
145         }
146         public boolean accept(File dir, String name) {
147             return (name.startsWith(prefix) && name.endsWith(suffix));
148         }
149     }
150 
151     /**
152      * Get all "management*ok.properties" files in the directory
153      * indicated by the "test.src" management property.
154      **/
155     private static File[] findConfigurationFilesOk() {
156         final String testSrc = System.getProperty("test.src");
157         final File dir = new File(testSrc);
158         final FilenameFilter filter =
159             new ConfigFilenameFilter("management_test","ok.properties");
160         return dir.listFiles(filter);
161     }
162 
163     /**
164      * Get all "management*ko.properties" files in the directory
165      * indicated by the "test.src" management property.
166      **/
167     private static File[] findConfigurationFilesKo() {
168         final String testSrc = System.getProperty("test.src");
169         final File dir = new File(testSrc);
170         final FilenameFilter filter =
171             new ConfigFilenameFilter("management_test","ko.properties");
172         return dir.listFiles(filter);
173     }
174 
175     /**
176      * List all MBeans and their attributes. Used to test communication
177      * with the Java M&M MBean Server.
178      * @return the number of queried MBeans.
179      */
180     public static int listMBeans(MBeanServerConnection server)
181         throws IOException {
182         return listMBeans(server,null,null);
183     }
184 
185     /**
186      * List all matching MBeans and their attributes.
187      * Used to test communication with the Java M&M MBean Server.
188      * @return the number of matching MBeans.
189      */
190     public static int listMBeans(MBeanServerConnection server,
191                                   ObjectName pattern, QueryExp query)
192         throws IOException {
193 
194         final Set names = server.queryNames(pattern,query);
195         for (final Iterator i=names.iterator(); i.hasNext(); ) {
196             ObjectName name = (ObjectName)i.next();
197             log.trace("listMBeans","Got MBean: "+name);
198             try {
199                 MBeanInfo info =
200                     server.getMBeanInfo((ObjectName)name);
201                 MBeanAttributeInfo[] attrs = info.getAttributes();
202                 if (attrs == null) continue;
203                 for (int j=0; j<attrs.length; j++) {
204                     if (attrs[j].isReadable()) {
205                         try {
206                             Object o =
207                                 server.getAttribute(name,attrs[j].getName());
208                             if (log.isDebugOn())
209                                 log.debug("listMBeans","\t\t" +
210                                           attrs[j].getName() +
211                                           " = "+o);
212                         } catch (Exception x) {
213                             log.trace("listMBeans","JmxClient failed to get " +
214                                       attrs[j].getName() + ": " + x);
215                             final IOException io =
216                                 new IOException("JmxClient failed to get " +
217                                                 attrs[j].getName());
218                             io.initCause(x);
219                             throw io;
220                         }
221                     }
222                 }
223             } catch (Exception x) {
224                log.trace("listMBeans",
225                          "JmxClient failed to get MBeanInfo: "  + x);
226                 final IOException io =
227                     new IOException("JmxClient failed to get MBeanInfo: "+x);
228                 io.initCause(x);
229                 throw io;
230             }
231         }
232         return names.size();
233     }
234 
235     /**
236      * Compute the full path name for a default file.
237      * @param basename basename (with extension) of the default file.
238      * @return ${JRE}/lib/management/${basename}
239      **/
240     private static String getDefaultFileName(String basename) {
241         final String fileSeparator = File.separator;
242         final StringBuffer defaultFileName =
243             new StringBuffer(System.getProperty("java.home")).
244             append(fileSeparator).append("lib").append(fileSeparator).
245             append("management").append(fileSeparator).
246             append(basename);
247         return defaultFileName.toString();
248     }
249 
250     /**
251      * Compute the full path name for a default file.
252      * @param basename basename (with extension) of the default file.
253      * @return ${JRE}/lib/management/${basename}
254      **/
255     private static String getDefaultStoreName(String basename) {
256         final String fileSeparator = File.separator;
257         final StringBuffer defaultFileName =
258             new StringBuffer(System.getProperty("test.src")).
259             append(fileSeparator).append("ssl").append(fileSeparator).
260             append(basename);
261         return defaultFileName.toString();
262     }
263 
264 
265     /**
266      * Parses the password file to read the credentials.
267      * Returns an ArrayList of arrays of 2 string:
268      * {<subject>, <password>}.
269      * If the password file does not exists, return an empty list.
270      * (File not found = empty file).
271      **/
272     private ArrayList readCredentials(String passwordFileName)
273         throws IOException {
274         final Properties pws = new Properties();
275         final ArrayList  result = new ArrayList();
276         final File f = new File(passwordFileName);
277         if (!f.exists()) return result;
278         FileInputStream fin = new FileInputStream(passwordFileName);
279         try {pws.load(fin);}finally{fin.close();}
280         for (Enumeration en=pws.propertyNames();en.hasMoreElements();) {
281             final String[] cred = new String[2];
282             cred[0]=(String)en.nextElement();
283             cred[1]=pws.getProperty(cred[0]);
284             result.add(cred);
285         }
286         return result;
287     }
288 
289 
290     /**
291      * Connect with the given url, using all given credentials in turn.
292      * A null entry in the useCredentials arrays indicate a connection
293      * where no credentials are used.
294      * @param url JMXServiceURL of the server.
295      * @param useCredentials An array of credentials (a credential
296      *        is a two String array, so this is an array of arrays
297      *        of strings:
298      *           useCredentials[i][0]=subject
299      *           useCredentials[i][1]=password
300      *        if useCredentials[i] == null means no credentials.
301      * @param expectConnectOk true if connection is expected to succeed
302      *    Note: if expectConnectOk=false and the test fails to connect
303      *          the number of failure is not incremented. Conversely,
304      *          if expectConnectOk=false and the test does not fail to
305      *          connect the number of failure is incremented.
306      * @param expectReadOk true if communication (listMBeans) is expected
307      *        to succeed.
308      *    Note: if expectReadOk=false and the test fails to read MBeans
309      *          the number of failure is not incremented. Conversely,
310      *          if expectReadOk=false and the test does not fail to
311      *          read MBeans the number of failure is incremented.
312      * @return number of failure.
313      **/
314     public int connectAndRead(JMXServiceURL url,
315                               Object[] useCredentials,
316                               boolean  expectConnectOk,
317                               boolean  expectReadOk)
318         throws IOException {
319 
320         int errorCount = 0;
321 
322         for (int i=0 ; i<useCredentials.length ; i++) {
323             final Map m = new HashMap();
324             final String[] credentials = (String[])useCredentials[i];
325             final String   crinfo;
326             if (credentials != null) {
327                 crinfo = "{"+credentials[0] + ", " + credentials[1] + "}";
328                 m.put(PropertyNames.CREDENTIALS,credentials);
329             } else {
330                 crinfo="no credentials";
331             }
332             log.trace("testCommunication","using credentials: " + crinfo);
333 
334             final JMXConnector c;
335             try {
336                 c = JMXConnectorFactory.connect(url,m);
337             } catch (IOException x ) {
338                 if (expectConnectOk) {
339                     final String err = "Connection failed for " + crinfo +
340                         ": " + x;
341                     System.out.println(err);
342                     log.trace("testCommunication",err);
343                     log.debug("testCommunication",x);
344                     errorCount++;
345                     continue;
346                 } else {
347                     System.out.println("Connection failed as expected for " +
348                                        crinfo + ": " + x);
349                     continue;
350                 }
351             } catch (RuntimeException x ) {
352                 if (expectConnectOk) {
353                     final String err = "Connection failed for " + crinfo +
354                         ": " + x;
355                     System.out.println(err);
356                     log.trace("testCommunication",err);
357                     log.debug("testCommunication",x);
358                     errorCount++;
359                     continue;
360                 } else {
361                     System.out.println("Connection failed as expected for " +
362                                        crinfo + ": " + x);
363                     continue;
364                 }
365             }
366             try {
367                 MBeanServerConnection conn =
368                     c.getMBeanServerConnection();
369                 if (log.isDebugOn()) {
370                     log.debug("testCommunication","Connection is:" + conn);
371                     log.debug("testCommunication","Server domain is: " +
372                               conn.getDefaultDomain());
373                 }
374                 final ObjectName pattern =
375                     new ObjectName("java.lang:type=Memory,*");
376                 final int count = listMBeans(conn,pattern,null);
377                 if (count == 0)
378                     throw new Exception("Expected at least one matching "+
379                                         "MBean for "+pattern);
380                 if (expectReadOk) {
381                     System.out.println("Communication succeeded " +
382                                        "as expected for "+
383                                        crinfo + ": found " + count
384                                        + ((count<2)?"MBean":"MBeans"));
385                 } else {
386                     final String err = "Expected failure didn't occur for " +
387                         crinfo;
388                     System.out.println(err);
389                     errorCount++;
390                 }
391             } catch (IOException x ) {
392                 if (expectReadOk) {
393                     final String err = "Communication failed with " + crinfo +
394                         ": " + x;
395                     System.out.println(err);
396                     log.trace("testCommunication",err);
397                     log.debug("testCommunication",x);
398                     errorCount++;
399                     continue;
400                 } else {
401                     System.out.println("Communication failed as expected for "+
402                                        crinfo + ": " + x);
403                     continue;
404                 }
405             } catch (RuntimeException x ) {
406                 if (expectReadOk) {
407                     final String err = "Communication failed with " + crinfo +
408                         ": " + x;
409                     System.out.println(err);
410                     log.trace("testCommunication",err);
411                     log.debug("testCommunication",x);
412                     errorCount++;
413                     continue;
414                 } else {
415                     System.out.println("Communication failed as expected for "+
416                                        crinfo + ": " + x);
417                 }
418             } catch (Exception x) {
419                 final String err = "Failed to read MBeans with " + crinfo +
420                     ": " + x;
421                 System.out.println(err);
422                 log.trace("testCommunication",err);
423                 log.debug("testCommunication",x);
424                 errorCount++;
425                 continue;
426             } finally {
427                 c.close();
428             }
429         }
430         return errorCount;
431     }
432 
433 
434     private void setSslProperties() {
435         final String defaultKeyStore =
436             getDefaultStoreName(DefaultValues.KEYSTORE);
437         final String defaultTrustStore =
438             getDefaultStoreName(DefaultValues.TRUSTSTORE);
439 
440         final String keyStore =
441             System.getProperty(PropertyNames.KEYSTORE, defaultKeyStore);
442         System.setProperty(PropertyNames.KEYSTORE,keyStore);
443         log.trace("setSslProperties",PropertyNames.KEYSTORE+"="+keyStore);
444 
445         final String password =
446             System.getProperty(PropertyNames.KEYSTORE_PASSWD,
447                                DefaultValues.KEYSTORE_PASSWD);
448         System.setProperty(PropertyNames.KEYSTORE_PASSWD,password);
449         log.trace("setSslProperties",
450                   PropertyNames.KEYSTORE_PASSWD+"="+password);
451 
452         final String trustStore =
453             System.getProperty(PropertyNames.TRUSTSTORE,
454                                defaultTrustStore);
455         System.setProperty(PropertyNames.TRUSTSTORE,trustStore);
456         log.trace("setSslProperties",
457                   PropertyNames.TRUSTSTORE+"="+trustStore);
458 
459         final String trustword =
460             System.getProperty(PropertyNames.TRUSTSTORE_PASSWD,
461                                DefaultValues.TRUSTSTORE_PASSWD);
462         System.setProperty(PropertyNames.TRUSTSTORE_PASSWD,trustword);
463         log.trace("setSslProperties",
464                   PropertyNames.TRUSTSTORE_PASSWD+"="+trustword);
465     }
466 
467     private void checkSslConfiguration() {
468         try {
469             final String defaultConf =
470                 getDefaultFileName(DefaultValues.CONFIG_FILE_NAME);
471             final String confname =
472                 System.getProperty(PropertyNames.CONFIG_FILE_NAME,defaultConf);
473 
474             final Properties props = new Properties();
475             final File conf = new File(confname);
476             if (conf.exists()) {
477                 FileInputStream fin = new FileInputStream(conf);
478                 try {props.load(fin);} finally {fin.close();}
479             }
480 
481             // Do we use SSL?
482             final String  useSslStr =
483                 props.getProperty(PropertyNames.USE_SSL,
484                                   DefaultValues.USE_SSL);
485             final boolean useSsl =
486                 Boolean.valueOf(useSslStr).booleanValue();
487 
488             log.debug("checkSslConfiguration",
489                       PropertyNames.USE_SSL+"="+useSsl+
490                       ": setting SSL");
491             // Do we use SSL client authentication?
492             final String  useSslClientAuthStr =
493                 props.getProperty(PropertyNames.SSL_NEED_CLIENT_AUTH,
494                                   DefaultValues.SSL_NEED_CLIENT_AUTH);
495             final boolean useSslClientAuth =
496                 Boolean.valueOf(useSslClientAuthStr).booleanValue();
497 
498             log.debug("checkSslConfiguration",
499                       PropertyNames.SSL_NEED_CLIENT_AUTH+"="+useSslClientAuth);
500 
501             // Do we use customized SSL cipher suites?
502             final String sslCipherSuites =
503                 props.getProperty(PropertyNames.SSL_ENABLED_CIPHER_SUITES);
504 
505             log.debug("checkSslConfiguration",
506                       PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +
507                       sslCipherSuites);
508 
509             // Do we use customized SSL protocols?
510             final String sslProtocols =
511                 props.getProperty(PropertyNames.SSL_ENABLED_PROTOCOLS);
512 
513             log.debug("checkSslConfiguration",
514                       PropertyNames.SSL_ENABLED_PROTOCOLS + "=" +
515                       sslProtocols);
516 
517             if (useSsl) setSslProperties();
518         } catch (Exception x) {
519             System.out.println("Failed to setup SSL configuration: " + x);
520             log.debug("checkSslConfiguration",x);
521         }
522     }
523 
524     /**
525      * Tests the server bootstraped at the given URL.
526      * Uses the system properties to determine which config file is used.
527      * Loads the config file to determine which password file is used.
528      * Loads the password file to find out wich credentials to use.
529      * Also checks that unregistered user/passwords are not allowed to
530      * connect when a password file is used.
531      *
532      * This method calls connectAndRead().
533      **/
534     public void testCommunication(JMXServiceURL url)
535         throws IOException {
536 
537         final String defaultConf =
538             getDefaultFileName(DefaultValues.CONFIG_FILE_NAME);
539         final String confname =
540             System.getProperty(PropertyNames.CONFIG_FILE_NAME,defaultConf);
541 
542         final Properties props = new Properties();
543         final File conf = new File(confname);
544         if (conf.exists()) {
545             FileInputStream fin = new FileInputStream(conf);
546             try {props.load(fin);} finally {fin.close();}
547         }
548 
549         // Do we use authentication?
550         final String  useAuthenticationStr =
551             props.getProperty(PropertyNames.USE_AUTHENTICATION,
552                               DefaultValues.USE_AUTHENTICATION);
553         final boolean useAuthentication =
554             Boolean.valueOf(useAuthenticationStr).booleanValue();
555 
556         // Get Password File
557         final String defaultPasswordFileName = Utils.convertPath(
558             getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME));
559         final String passwordFileName = Utils.convertPath(
560             props.getProperty(PropertyNames.PASSWORD_FILE_NAME,
561                               defaultPasswordFileName));
562 
563         // Get Access File
564         final String defaultAccessFileName = Utils.convertPath(
565             getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
566         final String accessFileName = Utils.convertPath(
567             props.getProperty(PropertyNames.ACCESS_FILE_NAME,
568                               defaultAccessFileName));
569 
570         if (useAuthentication) {
571             System.out.println("PasswordFileName: " + passwordFileName);
572             System.out.println("accessFileName: " + accessFileName);
573         }
574 
575         final Object[] allCredentials;
576         final Object[] noCredentials = { null };
577         if (useAuthentication) {
578             final ArrayList l = readCredentials(passwordFileName);
579             if (l.size() == 0) allCredentials = null;
580             else allCredentials = l.toArray();
581         } else allCredentials = noCredentials;
582 
583         int errorCount = 0;
584         if (allCredentials!=null) {
585             // Tests that the registered user/passwords are allowed to
586             // connect & read
587             //
588             errorCount += connectAndRead(url,allCredentials,true,true);
589         } else {
590             // Tests that no one is allowed
591             // connect & read
592             //
593             final String[][] someCredentials = {
594                 null,
595                 { "modify", "R&D" },
596                 { "measure", "QED" }
597             };
598             errorCount += connectAndRead(url,someCredentials,false,false);
599         }
600 
601         if (useAuthentication && allCredentials != noCredentials) {
602             // Tests that the registered user/passwords are not allowed to
603             // connect & read
604             //
605             final String[][] badCredentials = {
606                 { "bad.user", "R&D" },
607                 { "measure", "bad.password" }
608             };
609             errorCount += connectAndRead(url,badCredentials,false,false);
610         }
611         if (errorCount > 0) {
612             final String err = "Test " + confname + " failed with " +
613                 errorCount + " error(s)";
614             log.debug("testCommunication",err);
615             throw new RuntimeException(err);
616         }
617     }
618 
619 
620     /**
621      * Test the configuration indicated by `file'.
622      * Sets the appropriate System properties for config file and
623      * port and then calls ConnectorBootstrap.initialize().
624      * eventually cleans up by calling ConnectorBootstrap.terminate().
625      * @return null if the test succeeds, an error message otherwise.
626      **/
627     private String testConfiguration(File file,int port) {
628 
629         final String path;
630         try {
631             path=(file==null)?null:file.getCanonicalPath();
632         } catch(IOException x) {
633             final String err = "Failed to test configuration " + file +
634                 ": " + x;
635             log.trace("testConfiguration",err);
636             log.debug("testConfiguration",x);
637             return err;
638         }
639         final String config = (path==null)?"Default config file":path;
640 
641         System.out.println("***");
642         System.out.println("*** Testing configuration (port=" + port + "): "
643                            + path);
644         System.out.println("***");
645 
646         System.setProperty("com.sun.management.jmxremote.port",
647                            Integer.toString(port));
648         if (path != null)
649             System.setProperty("com.sun.management.config.file", path);
650         else
651             System.getProperties().remove("com.sun.management.config.file");
652 
653         log.trace("testConfiguration","com.sun.management.jmxremote.port="+port);
654         if (path != null && log.isDebugOn())
655             log.trace("testConfiguration",
656                       "com.sun.management.config.file="+path);
657 
658         checkSslConfiguration();
659 
660         final JMXConnectorServer cs;
661         try {
662             cs = ConnectorBootstrap.initialize();
663         } catch (AgentConfigurationError x) {
664             final String err = "Failed to initialize connector:" +
665                 "\n\tcom.sun.management.jmxremote.port=" + port +
666                 ((path!=null)?"\n\tcom.sun.management.config.file="+path:
667                  "\n\t"+config) +
668                 "\n\tError is: " + x;
669             log.trace("testConfiguration",err);
670             log.debug("testConfiguration",x);
671             return err;
672         } catch (Exception x) {
673             log.debug("testConfiguration",x);
674             return x.toString();
675         }
676 
677         try {
678             JMXServiceURL url =
679                 new JMXServiceURL("rmi",null,0,"/jndi/rmi://localhost:"+
680                                   port+"/jmxrmi");
681 
682             try {
683                 testCommunication(url);
684             } catch (Exception x) {
685                 final String err = "Failed to connect to agent {url="+url+
686                     "}: " + x;
687                 log.trace("testConfiguration",err);
688                 log.debug("testConfiguration",x);
689                 return err;
690             }
691         } catch (Exception x) {
692             final String err = "Failed to test configuration "+config+
693                 ": "+x;
694             log.trace("testConfiguration",err);
695             log.debug("testConfiguration",x);
696             return err;
697         } finally {
698             try {
699                 cs.stop();
700             } catch (Exception x) {
701                 final String err = "Failed to terminate: "+x;
702                 log.trace("testConfiguration",err);
703                 log.debug("testConfiguration",x);
704             }
705         }
706         System.out.println("Configuration " + config + " successfully tested");
707         return null;
708     }
709 
710     /**
711      * Test a configuration file which should make the bootstrap fail.
712      * The test is assumed to have succeeded if the bootstrap fails.
713      * @return null if the test succeeds, an error message otherwise.
714      **/
715     private String testConfigurationKo(File conf,int port) {
716         final String errStr = testConfiguration(conf,port+testPort++);
717         if (errStr == null) {
718             return "Configuration " +
719                 conf + " should have failed!";
720         }
721         System.out.println("Configuration " +
722                            conf + " failed as expected");
723         log.debug("runko","Error was: " + errStr);
724         return null;
725     }
726 
727     /**
728      * Test a configuration file. Determines whether the bootstrap
729      * should succeed or fail depending on the file name:
730      *     *ok.properties: bootstrap should succeed.
731      *     *ko.properties: bootstrap or connection should fail.
732      * @return null if the test succeeds, an error message otherwise.
733      **/
734     private String testConfigurationFile(String fileName) {
735         File file = new File(fileName);
736         final String portStr = System.getProperty("rmi.port","12424");
737         final int port       = Integer.parseInt(portStr);
738 
739         if (fileName.endsWith("ok.properties")) {
740             return testConfiguration(file,port+testPort++);
741         }
742         if (fileName.endsWith("ko.properties")) {
743             return testConfigurationKo(file,port+testPort++);
744         }
745         return fileName +
746             ": test file suffix must be one of [ko|ok].properties";
747     }
748 
749     /**
750      * Find all *ko.property files and test them.
751      * (see findConfigurationFilesKo() and testConfigurationKo())
752      * @throws RuntimeException if the test fails.
753      **/
754     public void runko() {
755         final String portStr = System.getProperty("rmi.port","12424");
756         final int port       = Integer.parseInt(portStr);
757         final File[] conf = findConfigurationFilesKo();
758         if ((conf == null)||(conf.length == 0))
759             throw new RuntimeException("No configuration found");
760 
761         String errStr;
762         for (int i=0;i<conf.length;i++) {
763             errStr = testConfigurationKo(conf[i],port+testPort++);
764             if (errStr != null) {
765                 throw new RuntimeException(errStr);
766             }
767         }
768 
769     }
770 
771     /**
772      * Find all *ok.property files and test them.
773      * (see findConfigurationFilesOk() and testConfiguration())
774      * @throws RuntimeException if the test fails.
775      **/
776     public void runok() {
777         final String portStr = System.getProperty("rmi.port","12424");
778         final int port       = Integer.parseInt(portStr);
779         final File[] conf = findConfigurationFilesOk();
780         if ((conf == null)||(conf.length == 0))
781             throw new RuntimeException("No configuration found");
782 
783         String errStr;
784         for (int i=0;i<conf.length;i++) {
785             errStr = testConfiguration(conf[i],port+testPort++);
786             if (errStr != null) {
787                 throw new RuntimeException(errStr);
788             }
789         }
790 
791         // FIXME: No jmxremote.password is not installed in JRE by default.
792         // - disable the following test case.
793         //
794         // Test default config
795         //
796         // errStr = testConfiguration(null,port+testPort++);
797         // if (errStr != null) {
798         //    throw new RuntimeException(errStr);
799         // }
800     }
801 
802     /**
803      * Finds all configuration files (*ok.properties and *ko.properties)
804      * and tests them.
805      * (see runko() and runok()).
806      * @throws RuntimeException if the test fails.
807      **/
808     public void run() {
809         runok();
810         runko();
811     }
812 
813     /**
814      * Tests the specified configuration files.
815      * If args[] is not empty, each element in args[] is expected to be
816      * a filename ending either by ok.properties or ko.properties.
817      * Otherwise, the configuration files will be automatically determined
818      * by looking at all *.properties files located in the directory
819      * indicated by the System property "test.src".
820      * @throws RuntimeException if the test fails.
821      **/
822     public void run(String args[]) {
823         if (args.length == 0) {
824             run() ; return;
825         }
826         for (int i=0; i<args.length; i++) {
827             final String errStr =testConfigurationFile(args[i]);
828             if (errStr != null) {
829                 throw new RuntimeException(errStr);
830             }
831         }
832     }
833 
834     /**
835      * Calls run(args[]).
836      * exit(1) if the test fails.
837      **/
838     public static void main(String args[]) {
839         RmiBootstrapTest manager = new RmiBootstrapTest();
840         try {
841             manager.run(args);
842         } catch (RuntimeException r) {
843             System.out.println("Test Failed: "+ r.getMessage());
844             System.exit(1);
845         } catch (Throwable t) {
846             System.out.println("Test Failed: "+ t);
847             t.printStackTrace();
848             System.exit(2);
849         }
850         System.out.println("**** Test  RmiBootstrap Passed ****");
851     }
852 
853 }